'use strict';

const { Controller } = require('egg');
const scopes = require('../data/scope');
const url = require('url');
const querystring = require('querystring');

function addQueryToUri(uriString, queryObject) {
  const uri = url.parse(uriString);
  const qs = querystring.decode(uri.query);
  for (const i in queryObject) {
    if (!queryObject.hasOwnProperty(i)) continue;
    qs[i] = queryObject[i];
  }
  return url.format({
    protocol: uri.protocol,
    hostname: uri.hostname,
    pathname: uri.pathname,
    port: uri.port,

    query: qs,
    slashes: true,
  });
}

class AuthorizeController extends Controller {
  async grant(ctx, client_id, redirect_uri, scope, state) {
    try {
      const code = await ctx.service.authorize.grant(ctx.session.id, client_id, redirect_uri, scope);
      const query = {
        code: code.code,
      };
      if (state) query.state = state;
      ctx.status = 302;
      ctx.set('location', addQueryToUri(redirect_uri, query));
    } catch (e) {
      const error = 'server_error';
      const error_description = 'Internal Server Error';
      const error_uri = 'https://gitee.com/zcxsythenew/AUGENBLICK';
      const query = { error, error_description, error_uri };
      if (state) query.state = state;
      ctx.status = 302;
      ctx.set('location', addQueryToUri(redirect_uri, query));
    }
  }

  async authorize() {
    const { ctx } = this;

    ctx.validate({
      client_id: 'string',
    }, ctx.query);

    let { client_id, redirect_uri, scope, state } = ctx.query;

    if (!ctx.service.authorize.matchClientAndUri(client_id, redirect_uri)) {
      ctx.status = 403;
      return;
    }
    if (redirect_uri === undefined) redirect_uri = ctx.service.authorize.defaultRedirectUri(client_id);

    try {
      ctx.validate({
        response_type: 'string',
      }, ctx.query);
    } catch (e) {
      const error = 'invalid_request';
      const error_description = e.message;
      const error_uri = 'https://gitee.com/zcxsythenew/AUGENBLICK';
      const query = { error, error_description, error_uri };
      if (state) query.state = state;
      ctx.status = 302;
      ctx.set('location', addQueryToUri(redirect_uri, query));
      return;
    }

    try {
      ctx.validate({
        response_type: [ 'code' ],
      }, ctx.query);
    } catch (e) {
      const error = 'unsupported_response_type';
      const error_description = e.message;
      const error_uri = 'https://gitee.com/zcxsythenew/AUGENBLICK';
      const query = { error, error_description, error_uri };
      if (state) query.state = state;
      ctx.status = 302;
      ctx.set('location', addQueryToUri(redirect_uri, query));
      return;
    }

    scope = scope || 'user_info';

    scope = scope.split(' ');
    for (const i in scope) {
      if (!scope.hasOwnProperty(i)) continue;
      if (scopes.indexOf(scope[i]) === -1) {
        const error = 'invalid_scope';
        const error_description = 'Valid scope includes ' + JSON.stringify(scopes) + '.';
        const error_uri = 'https://gitee.com/zcxsythenew/AUGENBLICK';
        const query = { error, error_description, error_uri };
        if (state) query.state = state;
        ctx.status = 302;
        ctx.set('location', addQueryToUri(redirect_uri, query));
        return;
      }
    }

    if (ctx.session.id) {
      if (ctx.service.authorize.authorizeNeeded(client_id)) {
        if (typeof ctx.session.scope !== 'undefined' && ctx.session.scope !== null) {
          if (ctx.session.scope === '') {
            const error = 'access_denied';
            const error_description = 'User rejected authorization.';
            const error_uri = 'https://gitee.com/zcxsythenew/AUGENBLICK';
            const query = { error, error_description, error_uri };
            if (state) query.state = state;
            ctx.status = 302;
            ctx.set('location', addQueryToUri(redirect_uri, query));
          } else {
            const target = ctx.session.scope.split(' ');
            const new_scope = [];
            for (const value of scope) {
              if (target.indexOf(value) !== -1) new_scope.push(value);
            }
            await this.grant(ctx, client_id, redirect_uri, new_scope, state);
          }
          ctx.session.scope = undefined;
        } else {
          const description = ctx.service.authorize.description(client_id);
          ctx.redirect('/index.html#/authorize?redirect=' + encodeURIComponent(ctx.originalUrl)
            + '&description=' + encodeURIComponent(description)
            + '&scope=' + encodeURIComponent(scope.join(' ')));
        }
      } else {
        await this.grant(ctx, client_id, redirect_uri, scope, state);
      }
    } else {
      ctx.redirect('/index.html#/login?redirect=' + encodeURIComponent(ctx.originalUrl));
    }
  }
}

module.exports = AuthorizeController;
